#ifndef DigitalIO_DL636F_h
#define DigitalIO_DL636F_h

#include <iostream>
#include <iomanip>

#include <errno.h>
#include <sys/types.h>

extern "C" {
#include "sis3100_vme_calls.h"
}

#include "IOError.h"
#include "VmeModule.h"

using namespace std;

/*
see:

http://www.physi.uni-heidelberg.de/physi/ew/Geraete/DL636F_DigitalIO/DL636F.pdf

for description of the DL636F Digital IO Module
*/

class DigitalIO_DL636F : public VmeModule
{
 public:
  static const int OUTPUT_OFFSET = 0x0;
  static const int INPUT_OFFSET = 0x80;

  DigitalIO_DL636F(int handle,unsigned int address)
      : VmeModule(handle,address) {}

  /// returns input
  u_int16_t getInput() throw(IOError)
  {
    u_int16_t i;
    int r;

    if( (r=vme_A16D16_read(handle,address+INPUT_OFFSET,&i)) != 0 ) {

				throw IOError("DigitalIO_DL636F::getInput():\n"
											"  vme_A16D16_read() returned non zero",r);
		}

    //status of input corresponds to 2nd bit
    return (i & 0x2) >> 1;
  }

  /// returns output 1
  unsigned int getO1() throw(IOError)
  {
    u_int16_t i;
    int r;

    if( (r=vme_A16D16_read(handle,address+OUTPUT_OFFSET,&i)) != 0 )
      throw IOError("DigitalIO_DL636F::getO1():\n"
		    "  vme_A16D16_read() returned non zero",r);

    return (i & 0x1);
  }

  /// returns output 2
  unsigned int getO2() throw(IOError)
  {
    u_int16_t i;
    int r;

    if( (r=vme_A16D16_read(handle,address+OUTPUT_OFFSET,&i)) != 0 )
      throw IOError("DigitalIO_DL636F::getO2():\n"
		    "  vme_A16D16_read() returned non zero",r);

    return (i & 0x2) >> 1;
  }

  /// returns output 3
  unsigned int getO3() throw(IOError)
  {
    u_int16_t i;
    int r;

    if( (r=vme_A16D16_read(handle,address+OUTPUT_OFFSET,&i)) != 0 )
      throw IOError("DigitalIO_DL636F::getO3():\n"
		    "  vme_A16D16_read() returned non zero",r);

    return (i & 0x4) >> 2;
  }

  /// returns output 4
  unsigned int getO4() throw(IOError)
  {
    u_int16_t i;
    int r;

    if( (r=vme_A16D16_read(handle,address+OUTPUT_OFFSET,&i)) != 0 )
      throw IOError("DigitalIO_DL636F::getO4():\n"
		    "  vme_A16D16_read() returned non zero",r);

    return (i & 0x8) >> 3;
  }

  /// returns all output at once (bit 0 = O1, bit 1 = O2, ...)
  u_int16_t getOutputs() throw(IOError)
  {
    u_int16_t i;
    int r;

    if( (r=vme_A16D16_read(handle,address+OUTPUT_OFFSET,&i)) != 0 )
      throw IOError("DigitalIO_DL636F::getOutputs():\n"
		    "  vme_A16D16_read() returned non zero",r);

    return i;
  }

  void setO1( int b ) throw(IOError)
  {
    int r;
    u_int16_t outputs;

    // read all outputs
    if( (r=vme_A16D16_read(handle,address+OUTPUT_OFFSET,&outputs)) != 0 )
      throw IOError("DigitalIO_DL636F::setO1():\n"
		    "  vme_A16D16_read() returned non zero",r);

    b &= 0x0001;
    outputs = (outputs & ~0x1) | b;

    ///set bit for output1 accordingly
    if( (r=vme_A16D16_write(handle,address+OUTPUT_OFFSET,outputs)) )
      throw IOError("DigitalIO_DL636F::setO1():\n"
		    "  vme_A16D16_write() returned non zero",r);
  }
  
  void setO2( int b ) throw(IOError)
  {
    int r;
    u_int16_t outputs;

    // read all outputs
    if( (r=vme_A16D16_read(handle,address+OUTPUT_OFFSET,&outputs)) != 0 )
      throw IOError("DigitalIO_DL636F::setO2():\n"
		    "  vme_A16D16_read() returned non zero",r);   

    b &= 0x0001; b <<= 1;
    outputs = (outputs & ~0x2) | b;

    ///set bit for output1 accordingly
    if( (r=vme_A16D16_write(handle,address+OUTPUT_OFFSET,outputs)) )
      throw IOError("DigitalIO_DL636F::setO2():\n"
		    "  vme_A16D16_write() returned non zero",r);
  }

  void setO3( int b ) throw(IOError)
  {
    int r;
    u_int16_t outputs;

    // read all outputs
    if( (r=vme_A16D16_read(handle,address+OUTPUT_OFFSET,&outputs)) != 0 )
      throw IOError("DigitalIO_DL636F::setO3():\n"
		    "  vme_A16D16_read() returned non zero",r);   

    b &= 0x0001; b <<= 2;
    outputs = (outputs & ~0x4) | b;

    ///set bit for output1 accordingly
    if( (r=vme_A16D16_write(handle,address+OUTPUT_OFFSET,outputs)) )
      throw IOError("DigitalIO_DL636F::setO3(): vme_A16D16_write() returned non zero",r);
  }

  void setO4( int b ) throw(IOError)
  {
    int r;
    u_int16_t outputs;

    // read all outputs
    if( (r=vme_A16D16_read(handle,address+OUTPUT_OFFSET,&outputs)) != 0 )
      throw IOError("DigitalIO_DL636F::setO4():\n"
		    "  vme_A16D16_read() returned non zero",r);   

    b &= 0x0001; b <<= 3;
    outputs = (outputs & ~0x8) | b;

    ///set bit for output2 accordingly
    if( (r=vme_A16D16_write(handle,address+OUTPUT_OFFSET,outputs)) )
      throw IOError("DigitalIO_DL636F::setO4():"
		    "  vme_A16D16_write() returned non zero",r);
  }

  /// set all outputs at once (bit 0 = O1, bit 1 = O2, ...)
  void setOutputs( u_int16_t o ) throw(IOError)
  {
    int r;

    if( (r=vme_A16D16_write(handle,address+OUTPUT_OFFSET,o)) ) {
				stringstream ss;

				ss << hex
					 << "DigitalIO_DL636F::setOutputs():\n"
					 << "  vme_A16D16_write() returned non zero\n"
					 << "  handle=0x" << handle
					 << "  address=0x" << address+OUTPUT_OFFSET
					 << "  data=0x" << (u_int32_t)o
					 << dec;

      throw IOError(ss.str().c_str(),r);
		}
         }
};

#endif
